Expand description
Proc macro anyhow, a combination of ideas from
anyhow
and
proc-macro-error
to improve proc macro
development, especially focused on the error handling.
Motivation
Error handling in proc-macros is unideal, as the top level functions of proc
macros can only return TokenStreams
both in success and failure case. This
means that I often write code like this, moving the actual implementation in
a separate function to be able to use the ergonomic rust error handling with
e.g., ?
.
use proc_macro2::TokenStream as TokenStream2;
#[proc_macro]
pub fn my_macro(input: TokenStream) -> TokenStream {
match actual_implementation(input.into()) {
Ok(output) => output,
Err(error) => error.into_compile_error(),
}
.into()
}
fn actual_implementation(input: TokenStream2) -> syn::Result<TokenStream2> {
// ..
}
Using the #[manyhow]
macro
To activate the error hadling, just add #[manyhow]
above any
proc macro implementation, reducing the above example to:
use manyhow::manyhow;
use proc_macro2::TokenStream as TokenStream2;
#[manyhow]
#[proc_macro]
fn my_macro(input: TokenStream2) -> syn::Result<TokenStream2> {
// ..
}
See Without macros to see what this expands to under the hood.
A proc macro function marked as #[manyhow]
can take and return any
TokenStream
, and can also return Result<TokenStream, E>
where E
implments ToTokensError
. As additional parameters a
dummy and/or emitter can
be specified.
The manyhow
attribute takes optional flags to configure its behavior.
When used for proc_macro
and proc_macro_attribute
,
#[manyhow(input_as_dummy, ...)]
will take the input of a function like
proc_macro
to initialize the [dummy &mut TokenStream
](#
dummy-mut-tokenstream) while #[manyhow(item_as_dummy, ...)]
on
proc_macro_attribute
will initialize the dummy with the annotated item.
The #[manyhow(impl_fn, ...)]
flag will put the actual macro implementation
in a separate function. Making it available for e.g., unit testing with
proc_macro_utils::assert_expansion!
.
#[manyhow(impl_fn)]
#[proc_macro]
pub fn actual_macro(input: TokenStream2) -> TokenStream2 {
// ...
}
// would expand to
#[proc_macro]
pub fn actual_macro(input: TokenStream) -> TokenStream {
actual_macro_impl(input.into()).into()
}
fn actual_macro_impl(input: TokenStream2) -> TokenStream2 {
// ...
}
Without macros
manyhow
can be used without proc macros, and they can be disabled by
adding manyhow
with default-features=false
.
The usage is more or less the same, though with some added boilerplate from
needing to invoke one of function
, attribute
or derive
directly.
While the examples use closures, functions can be passed in as well. The above example would then change to:
use proc_macro2::TokenStream as TokenStream2;
#[proc_macro]
pub fn my_macro(input: TokenStream) -> TokenStream {
manyhow::function(
input,
false,
|input: TokenStream2| -> syn::Result<TokenStream2> {
// ..
},
)
}
Emitter
and dummy
TokenStream
can also be used. function
and
attribute
take an additional boolean parameter controlling whether the
input/item will be used as initial dummy.
emitter: &mut Emitter
MacroHandler
s (the trait defining what closures/functions can be used
with manyhow
) can take a mutable reference to an Emitter
. This
allows collecting errors, but not fail immediately.
Emitter::into_result
can be used to return if an Emitter
contains
any values.
use manyhow::{manyhow, Emitter, ErrorMessage};
use proc_macro2::TokenStream as TokenStream2;
#[manyhow]
#[proc_macro]
fn my_macro(input: TokenStream2, emitter: &mut Emitter) -> manyhow::Result<TokenStream2> {
// ..
emitter.emit(ErrorMessage::call_site("A fun error!"));
emitter.into_result()?;
// ..
}
dummy: &mut TokenStream
MacroHandler
s also take a mutable reference to a TokenStream
, to
enable emitting some dummy code to be used in case the macro errors.
This allows either appending tokens e.g., with ToTokens::to_tokens
or
directly setting the dummy code e.g., *dummy = quote!{some tokens}
.
Crate features
Macros
- Exit by returning error, matching
anyhow::bail!
- Push an error to an emitter.
- Creates an
ErrorMessage
, comparable to theanyhow!
macro
Structs
- Allows emitting errors without returning.
- This crates Error type
- A single error message
- Error that does not expand to any
compile_error!
and therefor does not cause compilation to fail.
Traits
- Marker trait for
proc_macro::TokenStream
andproc_macro2::TokenStream
- Exposes
ErrorMessage::attachment
as a trait to allowResultExt::attachment
. - Allows to call
.join(..)
on anyimpl ToTokensError
- Implementation of a proc-macro
- Some utilities on
Result<T, impl ToTokensError>
- Error that can be converted to a
TokenStream
required to be returned by aMacroHandler
Functions
- Handles
proc_macro_attribute
implementation - Handles
proc_macro_derive
implementation - Handles function like
proc_macro
implementation
Type Definitions
- An alias for
Result
suited for use with this crate
Attribute Macros
- Attribute macro to remove boiler plate from proc macro entry points.